package codec

import (
	"bufio"
	"io"
	"math"

	p "nggs/network/protocol"
	protocol "nggs/network/protocol/protobuf/v1"
	link "nggs/network/tcp/v1"
)

const (
	PH            = 0
	maxLength     = math.MaxUint16
	sizeofLength  = 2
	sizeofPartial = 2 * 1024
)

type ProtoBufProtocol struct {
	protocol.IProtocol

	sizeofRecvBuffer int
	sizeofSendBuffer int
}

func ProtoBuf(sizeofRecvBuffer int, sizeofSendBuffer int,
	allocator p.IAllocator, encryptor p.IEncryptor, decryptor p.IDecryptor) *ProtoBufProtocol {

	proto := &ProtoBufProtocol{
		IProtocol:        protocol.New(allocator, encryptor, decryptor),
		sizeofRecvBuffer: sizeofRecvBuffer,
		sizeofSendBuffer: sizeofSendBuffer,
	}

	return proto
}

func (p *ProtoBufProtocol) NewCodec(rw io.ReadWriter) (link.Codec, error) {
	c := &protoBufCodec{
		p:      p,
		reader: bufio.NewReaderSize(rw, p.sizeofRecvBuffer),
		writer: bufio.NewWriterSize(rw, p.sizeofSendBuffer),
	}
	c.closer, _ = rw.(io.Closer)
	return c, nil
}

type protoBufCodec struct {
	p *ProtoBufProtocol

	reader *bufio.Reader
	writer *bufio.Writer
	closer io.Closer

	recvLengthBuffer [sizeofLength]byte
	recvBodyBuffer   []byte
	sendBuffer       []byte
}

func (c *protoBufCodec) Receive() (i interface{}, err error) {
	_, err = io.ReadFull(c.reader, c.recvLengthBuffer[:sizeofLength])
	if err != nil {
		return
	}

	// todo 大于2048字节的包要组合接收

	length := int(protocol.ByteOrder.Uint16(c.recvLengthBuffer[:sizeofLength]))
	if length > maxLength {
		return nil, &p.ErrTooLarge{}
	}

	bodyLength := length - sizeofLength
	if c.reader.Buffered() >= bodyLength {
		if c.recvBodyBuffer, err = c.reader.Peek(bodyLength); err != nil {
			return nil, err
		}
		defer func() {
			if _, discardErr := c.reader.Discard(bodyLength); discardErr != nil {
				err = discardErr
			}
		}()
	} else {
		c.recvBodyBuffer = c.p.Alloc(bodyLength)
		defer c.p.Free(c.recvBodyBuffer)

		if _, err = io.ReadFull(c.reader, c.recvBodyBuffer); err != nil {
			return nil, err
		}
	}

	iMsg, err := c.p.Decode(c.recvBodyBuffer)
	if err != nil {
		return nil, err
	}

	return iMsg, err
}

func (c *protoBufCodec) Send(i interface{}) (err error) {
	iMsg := i.(protocol.IMessage)

	length := sizeofLength + protocol.SizeofMessageID + int(iMsg.Size())
	if length > maxLength {
		return &p.ErrTooLarge{}
	}

	// todo 大于2048字节的包要拆分发送

	c.sendBuffer = c.p.Alloc(length)
	defer c.p.Free(c.sendBuffer)

	protocol.ByteOrder.PutUint16(c.sendBuffer[:sizeofLength], uint16(length))

	err = c.p.EncodeTo(iMsg, c.sendBuffer[sizeofLength:])
	if err != nil {
		return
	}

	_, err = c.writer.Write(c.sendBuffer)
	if err != nil {
		return
	}

	err = c.writer.Flush()

	return
}

func (c *protoBufCodec) Close() error {
	if c.closer != nil {
		return c.closer.Close()
	}
	return nil
}
